package uk.ac.bath.legacy;

import uk.ac.bath.environment.MachineIF;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Vector;
import uk.ac.bath.util.MyRandom;

/**
 *
 * This machine uses the previous output
 *
 * @author pjl
 */
abstract public class RecursiveMachine implements Cloneable, MachineIF {

 
    protected Vector<MappedOperator> unitList;
    protected int size;
    
    private VectorState state;
    private VectorState prevState;
    protected final PalleteIF pallete;

    protected RecursiveMachine(Vector<MappedOperator> unitList,PalleteIF pallete) {
        this.unitList = unitList;
        this.pallete=pallete;
        size = unitList.size();
        prevState = pallete.createVector(size); // new VectorState(size, pallete.protoValue);
        state = pallete.createVector(size); // new VectorState(size, pallete.protoValue);
    }

    public RecursiveMachine(int nOps, PalleteIF  pallete) {
        this.pallete=pallete;
        unitList = new Vector<MappedOperator>(nOps);
        for (int opCount = 0; opCount < nOps; opCount++) {
            Operator op = pallete.createRandom();
            MappedOperator mop =
                    MappedOperator.createRandomMappedOperator(opCount, nOps, op);
            unitList.add(mop);
        }
        size = nOps;
        prevState = pallete.createVector(size); //new VectorState(size, pallete.protoValue);
        state = pallete.createVector(size);// new VectorState(size, pallete.protoValue);
    }

    public int size() {
        return size;
    }

    protected MappedOperator cloneOpAt(int i) throws CloneNotSupportedException {
        return (MappedOperator) ((MappedOperator) (unitList.elementAt(i))).clone();
    }

    /**
     * Use this to get the current state.
     *
     *
     * @return
     */
//    public VectorState getState() {
//        return state;
//    }

    @Override
    abstract public Object clone() throws CloneNotSupportedException;

    public boolean isOn(int i){
        return state.at(i).doubleValue() > 0.5;

    }
    /**
     * Use the current state as input.
     * After tick the state is the output.
     *
     */
    public void tick() {
        VectorState tmp = prevState;
        prevState = state;
        state = tmp;
        state.setAll(0.0f);
        for (Enumeration e = unitList.elements(); e.hasMoreElements();) {
            ((MappedOperator) (e.nextElement())).tick(prevState, state);
        }
    }

    public void setIn(int i, float fVal) {
        if (i < 0 ) i+=size;
        state.at(i).set(fVal);
    }

    public void toString(StringWriter w) {
        for (Enumeration e = unitList.elements(); e.hasMoreElements();) {
            ((MappedOperator) (e.nextElement())).toString(w);
            w.write('\n');
        }
        w.write("End");
    }



    public float out(int i) {
        return (float) state.at(i).val;
    }



    public void mutate() {
        int offset = MyRandom.nextInt(size);

        Operator op = pallete.createRandom();

        MappedOperator mop =
                MappedOperator.createRandomMappedOperator(offset, size, op);
        unitList.setElementAt(mop, offset);
    }
}
